#include "customgraphicsscene.h"
#include <math.h>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <QDialog>
#include <QMessageBox>
#include <QGraphicsRectItem>
#include "../shape/action.h"
#include "../flow/arrowflow.h"
#include "../flow/noteflow.h"
#include "../shape/initialnode.h"
#include "../shape/finalnode.h"
#include "../shape/note.h"
#include "../shape/decision.h"
#include "../scene/selectiontool.h"
#include "../scene/shapetool.h"
#include "../scene/flowtool.h"
#include "../flow/tempshape.h"
#include "../xml/save_xml.h"
#include "../xml/load_xml.h"
#include "../flow/flowtext.h"

void CustomGraphicsScene::notifyStateChangeByUser()
{
	emit stateChangedByUser();
}

QString CustomGraphicsScene::toXML()
{
	return create_scene_xml(this, false, (this->views().count()==0? NULL : this->views()[0]));
}

CustomGraphicsScene *CustomGraphicsScene::fromXML (const QString &xml, CustomGraphicsScene *scene)
{
	scene->removeAllShapes();
	load_scene_xml(xml, scene);
	return scene;
}

void CustomGraphicsScene::adjustFlowsAndTempShapesSelection()
{
	QList<Shape *> shapes = this->shapes();
	for(int i=0; i<shapes.count(); ++i)
	{
		TempShape *ts;
		if (ts=dynamic_cast<TempShape *>(shapes[i]))
			if (ts->isSelected() && !ts->findFlow()->isSelected())
				ts->setSelected(false);
			else if (!ts->isSelected() && ts->findFlow()->isSelected())
				ts->setSelected(true);
	}
}

CustomGraphicsScene *CustomGraphicsScene::extractSelectionCopy()
{
	adjustFlowsAndTempShapesSelection();
	CustomGraphicsScene *scene = new CustomGraphicsScene();
	QList<QGraphicsItem *> selected = this->selectedItems();
	QList<Flow *> flows;
	QList<Shape *> shapes;
	QHash<Shape *, Shape *> hash;

	for(int i=0; i<selected.count(); ++i)
	{
		if (_shapes.contains(dynamic_cast<Shape *>(selected[i])))
		{
			Shape *curshape = dynamic_cast<Shape *>(selected[i]);
			Shape *newshape = curshape->clone();
			hash[curshape] = newshape;
			if (dynamic_cast<Flow *>(newshape))
				flows << dynamic_cast<Flow *>(newshape);
			else
				shapes << newshape;
		}
	}

	for(int i=flows.count()-1; i>=0; --i)
	{
		TempShape *t;
		if (!hash.contains(flows[i]->startShape()))
		{
			flows[i]->setStartShape(t=new TempShape(flows[i]->startShape()));
			shapes << t;
		}
		else flows[i]->setStartShape(hash[flows[i]->startShape()]);

		if (!hash.contains(flows[i]->endShape()))
		{
			flows[i]->setEndShape(t=new TempShape(flows[i]->endShape()));
			shapes << t;
		}
		else flows[i]->setEndShape(hash[flows[i]->endShape()]);
	}

	for(int i=0; i<shapes.count(); ++i)
		scene->addShape(shapes[i]);
	for(int i=0; i<flows.count(); ++i)
		scene->addShape(flows[i]);

	return scene;
}

QList<Shape *> CustomGraphicsScene::addOtherSceneItems (CustomGraphicsScene *other)
{
	QList<Shape *> newshapes = other->shapes();
	for(int i=0; i<newshapes.count(); ++i)
		newshapes[i]->setSelected(true);
	CustomGraphicsScene *otherclone = other->extractSelectionCopy();
	newshapes = otherclone->shapes();
	for(int i=0; i<newshapes.count(); ++i)
		this->addShape(otherclone->takeShape(newshapes[i]));
	delete otherclone;
	return newshapes;
}

CustomGraphicsScene::~CustomGraphicsScene()
{
	removeAllShapes();
	delete selection_tool;
	delete arrowflow_tool;
	delete noteflow_tool;
	delete action_tool;
	delete decision_tool;
	delete note_tool;
	delete initialnode_tool;
	delete finalnode_tool;
}

QList<Shape *> CustomGraphicsScene::selectedShapes()
{
	QList<Shape *> result;
	QList<QGraphicsItem *> items = selectedItems();
	for(int i=0; i<items.count(); ++i)
		if (_shapes.contains(dynamic_cast<Shape *>(items[i])))
			result << dynamic_cast<Shape *>(items[i]);
	return result;
}

CustomGraphicsScene *CustomGraphicsScene::cutSelectedShapes()
{
	adjustFlowsAndTempShapesSelection();
	CustomGraphicsScene *result = extractSelectionCopy();
	QList<Shape *> selected = selectedShapes();
	NoteFlow *nf;
	for(int i=selected.count()-1; i>=0; --i)
		if (nf = dynamic_cast<NoteFlow *>(selected[i]))
		{
			removeShape(nf),
			selected.removeAt(i);
		}

	Flow *f;
	for(int i=selected.count()-1; i>=0; --i)
		if (_shapes.contains(selected[i]))
			if (f = dynamic_cast<Flow *>(selected[i]))
			{
				removeShape(f),
				selected.removeAt(i);
			}

	for(int i=selected.count()-1; i>=0; --i)
	{
		if (_shapes.contains(selected[i]))
		{
			Shape *sh = selected[i];
			QList<Shape *> shapes = this->shapes();
			for(int j=0; j<shapes.count(); ++j)
			{
				Flow *f;
				if (f=dynamic_cast<Flow *>(shapes[j]))
				{
					TempShape *t;
					if (f->startShape()==sh)
						f->setStartShape(t=new TempShape(sh)), addShape(t);
					if (f->endShape()==sh)
						f->setEndShape(t=new TempShape(sh)), addShape(t);
				}
			}
			removeShape(sh);
			selected.removeAt(i);
		}
	}
	return result;
}


QList<Shape *> CustomGraphicsScene::shapes() const
{
	return _shapes;
}

void CustomGraphicsScene::addShape (Shape *shape)
{
	connect(shape, SIGNAL(stateIsAboutToBeChangedByUser()), this, SIGNAL(stateChangedByUser()));
	_shapes.append(shape);
	addItem(shape);
}

Shape *CustomGraphicsScene::takeShape (Shape *shape)
{
	if (_shapes.contains(shape))
	{
		disconnect(shape, SIGNAL(stateIsAboutToBeChangedByUser()), this, SIGNAL(stateChangedByUser()));
		_shapes.removeAll(shape);
		return shape;
	}
	return NULL;
}

void CustomGraphicsScene::removeSelectedShapes()
{
	adjustFlowsAndTempShapesSelection();
	QList<Shape *> selected = selectedShapes();
	QList<Shape *> flows;
	QList<Shape *> shapes;
	foreach(Shape *item, selected)
		if (dynamic_cast<Flow *>(item))
			flows << dynamic_cast<Flow *>(item);
		else
			shapes << dynamic_cast<Shape *>(item);
	for(int i=0; i<flows.count(); ++i)
		removeShape(flows[i]);
	for(int i=0; i<shapes.count(); ++i)
		removeShape(shapes[i]);
}

void CustomGraphicsScene::removeShape (Shape *shape)
{
	if (_shapes.contains(shape))
	{
		Flow *item;
		CustomGraphicsScene *scene = this;
		QList<Shape *> items; items.append(shape);
		if (item=dynamic_cast<Flow *>(shape))
		{
			if (dynamic_cast<TempShape *>(item->startShape()))
				items << item->startShape();
			if (dynamic_cast<TempShape *>(item->endShape()))
				items << item->endShape();
		}
		QList<Shape *> all = _shapes;
		bool found;
		do{
			found = false;
			for(int i=0; i<all.count(); ++i)
			{
				if (item=dynamic_cast<Flow *>(all[i]))
				{
					if ((items.contains(item->endShape()) || items.contains(item->startShape())) && !items.contains(item))
					{
						if (dynamic_cast<TempShape *>(item->startShape()))
							items << item->startShape();
						if (dynamic_cast<TempShape *>(item->endShape()))
							items << item->endShape();
						items << item, found = true;
					}
				}
			}
		}while(found);
		for(int i=items.count()-1; i>=0; --i)
		{
			scene->removeItem(items[i]);
			disconnect(shape, SIGNAL(stateIsAboutToBeChangedByUser()), this, SIGNAL(stateChangedByUser()));
			_shapes.removeAll(items[i]);
			delete items[i];
		}
	}
}

void CustomGraphicsScene::removeAllShapes()
{
	QPainterPath path;
	path.addRect(this->sceneRect());
	setSelectionArea(path);
	removeSelectedShapes();
	this->clear();
	QGraphicsRectItem *item;
	addItem(item = new QGraphicsRectItem(QRectF(0,0,1000,700)));
	item->setVisible(false);
}

CustomGraphicsScene::CustomGraphicsScene(QObject *parent)
{
	setItemIndexMethod(QGraphicsScene::NoIndex);
	selection_tool = new SelectionSceneTool();
	arrowflow_tool = new FlowSceneTool(CustomGraphicsScene::ARROWFLOW);
	noteflow_tool = new FlowSceneTool(CustomGraphicsScene::NOTEFLOW);
	action_tool = new ShapeSceneTool(CustomGraphicsScene::ACTION);
	decision_tool = new ShapeSceneTool(CustomGraphicsScene::DECISION);
	initialnode_tool = new ShapeSceneTool(CustomGraphicsScene::INITIAL);
	finalnode_tool = new ShapeSceneTool(CustomGraphicsScene::FINAL);
	note_tool = new ShapeSceneTool(CustomGraphicsScene::NOTE);

	setBackgroundBrush(Qt::white);
	_currentTool = NULL;
	setSceneTool(selection_tool);
	QGraphicsRectItem *item;
	addItem(item = new QGraphicsRectItem(QRectF(0,0,1000,700)));
	item->setVisible(false);
}

void CustomGraphicsScene::setTool (CustomGraphicsScene::EditorTool t)
{
	/* Тут будет большооой switch... */
	switch(_tool = t)
	{
		case SELECTION:
			setSceneTool(selection_tool);
			break;
		case ARROWFLOW:
			setSceneTool(arrowflow_tool);
			break;
		case NOTEFLOW:
			setSceneTool(noteflow_tool);
			break;
		case ACTION:
			setSceneTool(action_tool);
			break;
		case DECISION:
			setSceneTool(decision_tool);
			break;
		case INITIAL:
			setSceneTool(initialnode_tool);
			break;
		case FINAL:
			setSceneTool(finalnode_tool);
			break;
		case NOTE:
			setSceneTool(note_tool);
			break;
		default:
			// error, it can't be
			break;
	}
}

CustomGraphicsScene::EditorTool CustomGraphicsScene::tool() const
{
	return _tool;
}

SceneTool *CustomGraphicsScene::sceneTool() const
{
	return _currentTool;
}

/**
 * Следует учесть, что тулы не уничтожаются, в предположении, что с их сигналами
 * activated() и deactivated() будут связаны слоты, ответственные, например, за
 * состояние панели инструментов. Возможно, впрочем, что в будущем я это изменю
 * (скорее всего).
 */
bool CustomGraphicsScene::setSceneTool (SceneTool *tool)
{
	if (tool==_currentTool) return true;

	if (!_currentTool || _currentTool->deactivate(tool))
	{
		tool->activate(this, _currentTool);
		_currentTool = tool;
		emit toolChanged();
		return true;
	}

	emit toolChanged();
	return false;
}

void CustomGraphicsScene::mousePressEvent (QGraphicsSceneMouseEvent *event)
{
	if (_currentTool->mousePressEvent(event))
		QGraphicsScene::mousePressEvent(event);
}

void CustomGraphicsScene::mouseMoveEvent (QGraphicsSceneMouseEvent *event)
{
	if (_currentTool->mouseMoveEvent(event))
		QGraphicsScene::mouseMoveEvent(event);
}

void CustomGraphicsScene::mouseReleaseEvent (QGraphicsSceneMouseEvent *event)
{
	for(int i=0; i<items().count(); ++i)
	{
		if (items()[i]->parentItem()==NULL && !dynamic_cast<FlowText *>(items()[i]))
		{
			QGraphicsItem *sh;
			if (sh = dynamic_cast<QGraphicsItem *>(items()[i]))
			{
				const int gridsize = 18;
				QPointF pt = sh->pos();
				sh->setPos((pt.toPoint()/gridsize)*gridsize);
				ResizableShape *rsh;
				if (rsh = dynamic_cast<ResizableShape *>(sh))
				{
					QSizeF sz1 = ((rsh->size().toSize() + QSize(1, 1))/gridsize)*gridsize;
					if (sz1.width() < rsh->minSize().width())
						sz1.setWidth(rsh->minSize().width());
					if (sz1.height() < rsh->minSize().height())
						sz1.setHeight(rsh->minSize().height());
					rsh->setSize(sz1);
				}
			}
		}
	}
	update();
	if (_currentTool->mouseReleaseEvent(event))
		QGraphicsScene::mouseReleaseEvent(event);
}

void CustomGraphicsScene::mouseDoubleClickEvent (QGraphicsSceneMouseEvent *event)
{
	if (_currentTool->mouseDoubleClickEvent(event))
		QGraphicsScene::mouseDoubleClickEvent(event);
}

